#ifndef __BREAKREPLICATOR_SIMPLE_H__
#define __BREAKREPLICATOR_SIMPLE_H__

#pragma once

#include "BreakReplicator.h"

#if NET_USE_SIMPLE_BREAKAGE

struct SBreakEvent;
struct IProtocolBuilder;
class CBitArray;
class BreakStream;

class CObjectIdentifier
{
public:
	enum ObjType { k_unknown = 0, k_entity, k_static_entity };

public:
	void Reset();
	void SerializeWith(CBitArray& array, bool includeObjCenter);
	void CreateFromPhysicalEntity(const EventPhysMono* pMono);
	IPhysicalEntity* FindPhysicalEntity();
	IPhysicalEntity* GetPhysicsFromHoldingEntity();

public:
	ObjType m_objType;

	Vec3 m_objCenter;
	uint32 m_objHash;

	// When a static is first broken, the remaining pieces get placed into a temporary, holding
	// entity. This points to that entity. But we continue to identify the static object via
	// the pos+cent+vol+hash method, and then resolve the holding entity on the client through
	// the corresponding CObjectIdentifier. That way we dont have to have a net-bound entity, which
	// clogs the internal message queue in the low level network code.
	EntityId m_holdingEntity;
};


#define NET_MAX_PENDING_BREAKAGE_STREAMS 1024

class CBreakReplicator : public CNetMessageSinkHelper<CBreakReplicator, INetMessageSink>, public IBreakReplicator
{
public:
	// Hack needed by GameContext
	bool workaround_defineProtocolMode_server;
	void DefineProtocol( IProtocolBuilder * pBuilder );
public:
	CBreakReplicator(CGameContext* pGameCtx);
	virtual ~CBreakReplicator();
	void Reset();
	bool OnCreatePhysEntityPart( const EventPhysCreateEntityPart * pEvent );
	bool OnRemovePhysEntityParts( const EventPhysRemoveEntityParts * pEvent );
	bool OnRevealEntityPart( const EventPhysRevealEntityPart * pEvent );
	bool OnJointBroken( const EventPhysJointBroken * pEvent );
	void BeginRecordingPlaneBreak( const SBreakEvent& be, EventPhysMono* pMono );
	void BeginRecordingDeforminBreak( const SBreakEvent& be, EventPhysMono* pMono );
	void OnBrokeSomething( const SBreakEvent& be, EventPhysMono* pMono, bool isPlane );

	void OnEndFrame();
	void OnSpawn( IEntity *pEntity, SEntitySpawnParams &params );
	void OnSpawn( IEntity *pEntity, IPhysicalEntity* pPhysEntity, IPhysicalEntity* pSrcPhysEntity );
	void OnRemove( IEntity *pEntity );
 	void RemoveEntity( IEntity *pEntity );
	void SendPayloads();
	void PushPlayback(BreakStream* pStream);
	int PlaybackStream(BreakStream* pStream);
	void PlaybackBreakage();
	void* ReceiveSimpleBreakage( TSerialize ser );                                          // Serialise a received a stream
	void* SerialiseBreakage( TSerialize ser, BreakStream* pStream );
	void PlaybackSimpleBreakage( void* userData, INetBreakageSimplePlaybackPtr pBreakage );  // Callback from low levek to push a stream for pending playback
	VIRTUAL const EventPhysRemoveEntityParts * GetRemovePartEvents(int & iNumEvents);

	int PushBackNewStream(const EventPhysMono* pMono, BreakStream* pStream);
	int GetBreakIndex(const EventPhysMono* pMono, bool add);
	int GetIdentifier(CObjectIdentifier* identifier, const IPhysicalEntity* pent, bool add);

 	void OnReuse( IEntity *pEntity, SEntitySpawnParams &params ) {}
 	void OnStartFrame() {}
 	void PlaybackBreakage( int breakId, INetBreakagePlaybackPtr pBreakage ) { PlaybackBreakage(); }
 	void GetMemoryStatistics( ICrySizer * s ) {}

	static CBreakReplicator* Get() { return m_pThis; }
	static IBreakReplicator * GetIBreakReplicator() { return static_cast<IBreakReplicator*>(m_pThis); }
	static void RegisterClasses();
	static int OnRevealEntityPart( const EventPhys *pEvent );
	static int OnJointBroken( const EventPhys *pEvent );
	static int OnCreatePhysEntityPart( const EventPhys *pEvent );
	static int OnRemovePhysEntityParts( const EventPhys *pEvent );
	
public:

	// A list of streams, indexed by the breakIdx
	DynArray<BreakStream*> m_streams;

	DynArray<CObjectIdentifier> m_identifiers;			// We need to keep a list of identifiers on all machines for entity syncing purposes (instead of using CryNetwork)

	int m_activeStartIdx;			// The index of the first active stream that is recording/playing

	int m_numPendingStreams;
	BreakStream* m_pendingStreams[NET_MAX_PENDING_BREAKAGE_STREAMS];	 // For playback, only pop one stream at a time for a physics entity

	std::vector<EventPhysRemoveEntityParts> m_removePartEvents;
	std::vector<EntityId> m_entitiesToRemove;

	static CBreakReplicator* m_pThis;
	static int	m_accurateWorldPosNumBits;
	static int	m_inaccurateWorldPosNumBits;
};


#endif // NET_USE_SIMPLE_BREAKAGE

#endif
